﻿namespace ThoughtWorks.CruiseControl.Core.Tasks
{
    using Exortech.NetReflector;
    using System;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Text;
    using ThoughtWorks.CruiseControl.Core;
    using ThoughtWorks.CruiseControl.Core.Util;

    [ReflectorType("msbuild")]
    public class MsBuildTask : BaseExecutableTask
    {
        private string buildArgs;
        private string executable;
        private string logger;
        private ProcessPriorityClass priority;
        private string projectFile;
        private string targets;
        private int timeout;
        private string workingDirectory;

        public const int DefaultTimeout = 600;
        private readonly IExecutionEnvironment executionEnvironment;
        public readonly Guid LogFileId;
        public const string LogFilename = "msbuild-results-{0}.xml";
        private IShadowCopier shadowCopier;

        public MsBuildTask() : this(new ProcessExecutor(), new ExecutionEnvironment(), new DefaultShadowCopier())
        {
        }

        public MsBuildTask(ProcessExecutor executor, IExecutionEnvironment executionEnvironment, IShadowCopier shadowCopier)
        {
            this.LogFileId = Guid.NewGuid();
            base.executor = executor;
            this.executionEnvironment = executionEnvironment;
            this.shadowCopier = shadowCopier;
            this.Executable = this.GetDefaultExecutable();
            this.Timeout = 600;
            this.Priority = ProcessPriorityClass.Normal;
        }

        private static string CheckAndQuoteLoggerSetting(string logger)
        {
            if (logger.IndexOf(';') > -1)
            {
                Log.Error("The <logger> setting contains semicolons. Only commas are allowed.");
                throw new CruiseControlException("The <logger> setting contains semicolons. Only commas are allowed.");
            }
            bool flag = false;
            StringBuilder builder = new StringBuilder();
            foreach (string str in logger.Split(new char[] { ',' }))
            {
                if (str.IndexOf(' ') > -1)
                {
                    if (flag)
                    {
                        Log.Error(
                            "The <logger> setting contains multiple spaces. Only the assembly name is allowed to contain spaces.");
                        throw new CruiseControlException(
                            "The <logger> setting contains multiple spaces. Only the assembly name is allowed to contain spaces.");
                    }
                    builder.Append(StringUtil.AutoDoubleQuoteString(str));
                    flag = true;
                }
                else
                {
                    builder.Append(str);
                }
                builder.Append(",");
            }
            return builder.ToString().TrimEnd(new char[] { ',' });
        }

        protected override bool Execute(IIntegrationResult result)
        {
            string information = !string.IsNullOrEmpty(base.Description)
                                     ? base.Description
                                     : string.Format(CultureInfo.CurrentCulture, "Executing MSBuild :BuildFile: {0}",
                                                     this.ProjectFile);

            result.BuildProgressInformation.SignalStartRunTask(information);

            ProcessInfo info = this.CreateProcessInfo(result);
            ProcessResult buildResult = base.TryToRun(info, result);
            string path = this.MsBuildOutputFile(result);
            if (File.Exists(path))
            {
                result.AddTaskResult(new FileTaskResult(path));
            }
            result.AddTaskResult(new ProcessTaskResult(buildResult, true));
            if (buildResult.TimedOut)
            {
                result.AddTaskResult(BaseExecutableTask.MakeTimeoutBuildResult(info));
            }
            return buildResult.Succeeded;
        }

        private string GetDefaultExecutable()
        {
            if (this.executionEnvironment.IsRunningOnWindows)
            {
                return Path.Combine(this.executionEnvironment.RuntimeDirectory, "MSBuild.exe");
            }
            return "xbuild";
        }

        private string GetLoggerArgs(IIntegrationResult result)
        {
            ProcessArgumentBuilder builder = new ProcessArgumentBuilder();
            builder.Append("/l:");
            if (string.IsNullOrEmpty(this.Logger))
            {
                string str = this.shadowCopier.RetrieveFilePath("ThoughtWorks.CruiseControl.MsBuild.dll");
                if (!string.IsNullOrEmpty(str))
                {
                    builder.Append(StringUtil.AutoDoubleQuoteString(str) + ";");
                }
            }
            else
            {
                builder.Append(CheckAndQuoteLoggerSetting(this.Logger) + ";");
            }
            builder.Append(StringUtil.AutoDoubleQuoteString(this.MsBuildOutputFile(result)));
            return builder.ToString();
        }

        protected override string GetProcessArguments(IIntegrationResult result)
        {
            ProcessArgumentBuilder builder = new ProcessArgumentBuilder();
            builder.AddArgument("/nologo");
            if (!string.IsNullOrEmpty(this.Targets))
            {
                builder.AddArgument("/t:");
                string text = string.Empty;
                foreach (string str2 in this.Targets.Split(new char[] { ';' }))
                {
                    if ((text == null) || (text.Length != 0))
                    {
                        text = string.Format(CultureInfo.CurrentCulture,
                                             "{0};{1}",
                                             text, StringUtil.AutoDoubleQuoteString(str2));
                    }
                    else
                    {
                        text = StringUtil.AutoDoubleQuoteString(str2);
                    }
                }
                builder.Append(text);
            }
            builder.AppendArgument(GetPropertyArgs(result));
            builder.AppendArgument(this.BuildArgs);
            builder.AddArgument(this.ProjectFile);
            builder.AppendArgument(this.GetLoggerArgs(result));
            return builder.ToString();
        }

        protected override string GetProcessBaseDirectory(IIntegrationResult result)
        {
            return result.BaseFromWorkingDirectory(this.WorkingDirectory);
        }

        protected override string GetProcessFilename()
        {
            if (!string.IsNullOrEmpty(this.Executable))
            {
                return this.Executable;
            }
            return this.GetDefaultExecutable();
        }

        protected override ProcessPriorityClass GetProcessPriorityClass()
        {
            return this.Priority;
        }

        protected override int GetProcessTimeout()
        {
            return (this.Timeout * 1000);
        }

        private static string GetPropertyArgs(IIntegrationResult result)
        {
            ProcessArgumentBuilder builder = new ProcessArgumentBuilder();
            builder.Append("/p:");
            int num = 0;
            foreach (string str in result.IntegrationProperties.Keys)
            {
                if (num > 0)
                {
                    builder.Append(";");
                }
                builder.Append(string.Format(CultureInfo.CurrentCulture,
                                             "{0}={1}",
                                                     str,
                                                     StringUtil.AutoDoubleQuoteString(
                                                         StringUtil.IntegrationPropertyToString(
                                                             result.IntegrationProperties[str]))
                                                 ));
                num++;
            }
            return builder.ToString();
        }

        private string MsBuildOutputFile(IIntegrationResult result)
        {
            return Path.Combine(result.ArtifactDirectory,
                                string.Format(CultureInfo.CurrentCulture,
                                              "msbuild-results-{0}.xml",
                                              this.LogFileId));
        }

        [ReflectorProperty("buildArgs", Required=false)]
        public string BuildArgs
        {
            get
            {
                return this.buildArgs;
            }
            set
            {
                this.buildArgs = value;
            }
        }

        [ReflectorProperty("executable", Required=false)]
        public string Executable
        {
            get
            {
                return this.executable;
            }
            set
            {
                this.executable = value;
            }
        }

        [ReflectorProperty("logger", Required=false)]
        public string Logger
        {
            get
            {
                return this.logger;
            }
            set
            {
                this.logger = value;
            }
        }

        [ReflectorProperty("priority", Required=false)]
        public ProcessPriorityClass Priority
        {
            get
            {
                return this.priority;
            }
            set
            {
                this.priority = value;
            }
        }

        [ReflectorProperty("projectFile", Required=false)]
        public string ProjectFile
        {
            get
            {
                return this.projectFile;
            }
            set
            {
                this.projectFile = value;
            }
        }

        [ReflectorProperty("targets", Required=false)]
        public string Targets
        {
            get
            {
                return this.targets;
            }
            set
            {
                this.targets = value;
            }
        }

        [ReflectorProperty("timeout", Required=false)]
        public int Timeout
        {
            get
            {
                return this.timeout;
            }
            set
            {
                this.timeout = value;
            }
        }

        [ReflectorProperty("workingDirectory", Required=false)]
        public string WorkingDirectory
        {
            get
            {
                return this.workingDirectory;
            }
            set
            {
                this.workingDirectory = value;
            }
        }
    }
}

